import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import requests
import json
import os
import ssl
from threading import Thread
import time

class HttpsMediaDownloaderApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Cannice HTTPS媒体文件下载器V0.1")
        self.root.geometry("800x600")
        self.root.resizable(True, True)

        # 设置服务器地址和端口
        self.server_ip = tk.StringVar(value="192.168.43.1")  # 默认IP
        self.server_port = tk.StringVar(value="8080")  # 根据wlan_scenario.c设置默认端口为8080
        self.download_dir = tk.StringVar(value=os.path.expanduser("~/Downloads"))
        self.cert_path = tk.StringVar(value=os.path.join(os.getcwd(), "client.crt"))
        self.allow_self_signed = tk.BooleanVar(value=False)  # 添加允许自签名证书选项
        self.files = []
        self.is_loading = False

        # 创建UI
        self.create_widgets()

    def create_widgets(self):
        # 服务器设置区域
        settings_frame = ttk.LabelFrame(self.root, text="服务器设置")
        settings_frame.pack(fill="x", padx=10, pady=5)

        ttk.Label(settings_frame, text="服务器IP:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
        ttk.Entry(settings_frame, textvariable=self.server_ip, width=15).grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(settings_frame, text="端口:").grid(row=0, column=2, padx=5, pady=5, sticky="w")
        ttk.Entry(settings_frame, textvariable=self.server_port, width=8).grid(row=0, column=3, padx=5, pady=5)

        ttk.Button(settings_frame, text="获取文件列表", command=self.get_file_list).grid(row=0, column=4, padx=10, pady=5)

        # 证书设置
        ttk.Label(settings_frame, text="证书路径:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
        ttk.Entry(settings_frame, textvariable=self.cert_path, width=30).grid(row=1, column=1, columnspan=3, padx=5, pady=5, sticky="ew")
        ttk.Button(settings_frame, text="浏览", command=self.browse_cert_file).grid(row=1, column=4, padx=10, pady=5)

        # 添加允许自签名证书选项
        ttk.Checkbutton(settings_frame, text="允许自签名证书(不安全)", variable=self.allow_self_signed).grid(row=3, column=0, columnspan=5, padx=5, pady=5, sticky="w")

        # 下载目录设置
        ttk.Label(settings_frame, text="下载目录:").grid(row=2, column=0, padx=5, pady=5, sticky="w")
        ttk.Entry(settings_frame, textvariable=self.download_dir, width=30).grid(row=2, column=1, columnspan=3, padx=5, pady=5, sticky="ew")
        ttk.Button(settings_frame, text="浏览", command=self.browse_download_dir).grid(row=2, column=4, padx=10, pady=5)

        # 文件列表区域
        files_frame = ttk.LabelFrame(self.root, text="文件列表")
        files_frame.pack(fill="both", expand=True, padx=10, pady=5)

        # 创建Treeview组件
        columns = ("name", "size", "date")
        self.file_tree = ttk.Treeview(files_frame, columns=columns, show="headings")

        # 设置列标题和宽度
        self.file_tree.heading("name", text="文件名")
        self.file_tree.column("name", width=300)

        self.file_tree.heading("size", text="大小")
        self.file_tree.column("size", width=100, anchor=tk.E)

        self.file_tree.heading("date", text="修改日期")
        self.file_tree.column("date", width=200)

        # 滚动条
        scrollbar = ttk.Scrollbar(files_frame, orient="vertical", command=self.file_tree.yview)
        self.file_tree.configure(yscroll=scrollbar.set)
        scrollbar.pack(side="right", fill="y")
        self.file_tree.pack(fill="both", expand=True)

        # 绑定双击事件
        self.file_tree.bind("<Double-1>", self.on_file_double_click)

        # 下载按钮
        self.download_button = ttk.Button(self.root, text="下载选中文件", command=self.download_selected_file)
        self.download_button.pack(pady=5)

        # 状态栏
        self.status_var = tk.StringVar(value="就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)
        status_bar.pack(side="bottom", fill="x")

    def browse_cert_file(self):
        file_path = filedialog.askopenfilename(filetypes=[("证书文件", "*.crt *.pem")])
        if file_path:
            self.cert_path.set(file_path)

    def browse_download_dir(self):
        directory = filedialog.askdirectory()
        if directory:
            self.download_dir.set(directory)

    def get_file_list(self):
        if self.is_loading:
            return

        self.is_loading = True
        self.status_var.set("正在获取文件列表...")

        # 清空现有列表
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        self.files = []

        # 在新线程中执行网络请求
        Thread(target=self._fetch_file_list).start()

    def _fetch_file_list(self):
        try:
            # 使用HTTPS协议
            server_url = f"https://{self.server_ip.get()}:{self.server_port.get()}/media-list"

            # 检查证书文件是否存在
            cert_file = self.cert_path.get()
            if not os.path.exists(cert_file):
                error_msg = f"证书文件不存在: {cert_file}\n\n请确保证书已正确复制到指定位置，或通过'浏览'按钮选择正确的证书文件。"
                self.root.after(0, lambda: messagebox.showerror("错误", error_msg))
                self.root.after(0, lambda: self.status_var.set("证书文件不存在"))
                return

            # 配置证书验证选项
            verify_option = cert_file
            if self.allow_self_signed.get():
                # 允许自签名证书，不验证证书有效性
                # 创建不验证证书的SSL上下文
                ssl_context = ssl.create_default_context()
                ssl_context.check_hostname = False
                ssl_context.verify_mode = ssl.CERT_NONE
                # 使用会话并配置SSL上下文
                session = requests.Session()
                # 直接在请求中使用verify=False来禁用证书验证
                response = session.get(server_url, timeout=10, verify=False, headers={'Connection': 'close'})
            else:
                # 正常验证证书
                response = requests.get(server_url, timeout=10, verify=cert_file)
            response.raise_for_status()

            # 解析JSON响应
            response_data = json.loads(response.text)
            self.files = response_data.get("contents", [])

            # 更新UI
            self.root.after(0, self._update_file_list_ui)
            self.root.after(0, lambda: self.status_var.set(f"找到 {len(self.files)} 个文件"))

        except Exception as e:
            self.root.after(0, lambda: messagebox.showerror("错误", f"获取文件列表失败: {str(e)}"))
            self.root.after(0, lambda: self.status_var.set("获取文件列表失败"))
        finally:
            self.is_loading = False

    def _format_file_size(self, size_bytes):
        """将字节数格式化为KB, MB, GB等"""
        if isinstance(size_bytes, str):
            return size_bytes
        try:
            size_bytes = int(size_bytes)
            if size_bytes < 1024:
                return f"{size_bytes} B"
            elif size_bytes < 1024 * 1024:
                return f"{size_bytes / 1024:.2f} KB"
            elif size_bytes < 1024 * 1024 * 1024:
                return f"{size_bytes / (1024 * 1024):.2f} MB"
            else:
                return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"
        except:
            return "未知大小"

    def _format_date(self, date_str):
        """格式化日期字符串"""
        if not date_str or date_str == "1980-01-06T00:00:00+0000":
            return "未知日期"
        try:
            # 解析ISO格式日期
            from datetime import datetime
            dt = datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%S+0000")
            # 转换为本地时间格式
            return dt.strftime("%Y-%m-%d %H:%M:%S")
        except:
            return date_str

    def _update_file_list_ui(self):
        for file in self.files:
            # 提取文件信息
            if isinstance(file, dict):
                name = file.get("name", "未知文件名")
                size = self._format_file_size(file.get("file_size", "未知大小"))
                date = self._format_date(file.get("time", "未知日期"))
            else:
                # 如果file不是字典，假设它是文件名
                name = str(file)
                size = "未知大小"
                date = "未知日期"

            # 添加到Treeview
            self.file_tree.insert("", "end", values=(name, size, date))

    def on_file_double_click(self, event):
        self.download_selected_file()

    def download_selected_file(self):
        selected_items = self.file_tree.selection()
        if not selected_items:
            messagebox.showinfo("提示", "请先选择要下载的文件")
            return

        # 获取选中的文件名
        item = selected_items[0]
        file_name = self.file_tree.item(item, "values")[0]

        self.download_file(file_name)

    def download_file(self, file_name):
        if not os.path.exists(self.download_dir.get()):
            try:
                os.makedirs(self.download_dir.get())
            except Exception as e:
                messagebox.showerror("错误", f"创建下载目录失败: {str(e)}")
                return

        # 检查证书文件是否存在
        cert_file = self.cert_path.get()
        if not os.path.exists(cert_file):
            error_msg = f"证书文件不存在: {cert_file}\n\n请确保证书已正确复制到指定位置，或通过'浏览'按钮选择正确的证书文件。"
            messagebox.showerror("错误", error_msg)
            return

        # 显示下载进度对话框
        self.progress_window = tk.Toplevel(self.root)
        self.progress_window.title(f"下载 {file_name}")
        self.progress_window.geometry("400x100")
        self.progress_window.resizable(False, False)
        self.progress_window.transient(self.root)
        self.progress_window.grab_set()

        ttk.Label(self.progress_window, text=f"正在下载: {file_name}").pack(padx=10, pady=5)

        self.progress_var = tk.DoubleVar()
        progress_bar = ttk.Progressbar(self.progress_window, variable=self.progress_var, length=380)
        progress_bar.pack(padx=10, pady=5)

        self.progress_percent = tk.StringVar(value="0%")
        ttk.Label(self.progress_window, textvariable=self.progress_percent).pack(pady=5)

        # 在新线程中执行下载
        Thread(target=self._download_file_thread, args=(file_name, cert_file)).start()

    def _download_file_thread(self, file_name, cert_file):
        try:
            server_url = f"https://{self.server_ip.get()}:{self.server_port.get()}/media/{file_name}"
            save_path = os.path.join(self.download_dir.get(), file_name)

            # 配置证书验证选项
            if self.allow_self_signed.get():
                # 允许自签名证书，不验证证书有效性
                # 使用会话并直接禁用证书验证
                session = requests.Session()
                with session.get(server_url, stream=True, timeout=30, verify=False) as response:
                    response.raise_for_status()
                    # 获取文件总大小
                    total_size = int(response.headers.get("content-length", 0))
                    downloaded_size = 0

                    # 写入文件
                    with open(save_path, "wb") as f:
                        for chunk in response.iter_content(chunk_size=8192):
                            if chunk:
                                f.write(chunk)
                                downloaded_size += len(chunk)

                                # 更新进度
                                if total_size > 0:
                                    progress = (downloaded_size / total_size) * 100
                                    self.root.after(0, lambda p=progress: self.progress_var.set(p))
                                    self.root.after(0, lambda p=progress: self.progress_percent.set(f"{p:.1f}%"))
            else:
                # 正常验证证书
                with requests.get(server_url, stream=True, timeout=30, verify=cert_file) as response:
                    response.raise_for_status()

                    # 获取文件总大小
                    total_size = int(response.headers.get("content-length", 0))
                    downloaded_size = 0

                # 写入文件
                with open(save_path, "wb") as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded_size += len(chunk)

                            # 更新进度
                            if total_size > 0:
                                progress = (downloaded_size / total_size) * 100
                                self.root.after(0, lambda p=progress: self.progress_var.set(p))
                                self.root.after(0, lambda p=progress: self.progress_percent.set(f"{p:.1f}%"))

            # 下载完成
            self.root.after(0, self.progress_window.destroy)
            self.root.after(0, lambda: messagebox.showinfo("成功", f"文件已下载到: {save_path}"))
            self.root.after(0, lambda: self.status_var.set(f"已下载: {file_name}"))

        except Exception as e:
            self.root.after(0, self.progress_window.destroy)
            self.root.after(0, lambda: messagebox.showerror("错误", f"下载失败: {str(e)}"))
            self.root.after(0, lambda: self.status_var.set("下载失败"))

if __name__ == "__main__":
    root = tk.Tk()
    app = HttpsMediaDownloaderApp(root)
    root.mainloop()